//-----------------------------------------------------------------------------
//
// (c) Copyright 2012-2013 Xilinx, Inc. All rights reserved.
//
// This file contains confidential and proprietary information of Xilinx, Inc.
// and is protected under U.S. and international copyright and other
// intellectual property laws.
//
// DISCLAIMER
//
// This disclaimer is not a license and does not grant any rights to the
// materials distributed herewith. Except as otherwise provided in a valid
// license issued to you by Xilinx, and to the maximum extent permitted by
// applicable law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL
// FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS,
// IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
// MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE;
// and (2) Xilinx shall not be liable (whether in contract or tort, including
// negligence, or under any other theory of liability) for any loss or damage
// of any kind or nature related to, arising under or in connection with these
// materials, including for any direct, or any indirect, special, incidental,
// or consequential loss or damage (including loss of data, profits, goodwill,
// or any type of loss or damage suffered as a result of any action brought by
// a third party) even if such damage or loss was reasonably foreseeable or
// Xilinx had been advised of the possibility of the same.
//
// CRITICAL APPLICATIONS
//
// Xilinx products are not designed or intended to be fail-safe, or for use in
// any application requiring fail-safe performance, such as life-support or
// safety devices or systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any other
// applications that could lead to death, personal injury, or severe property
// or environmental damage (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and liability of any use of
// Xilinx products in Critical Applications, subject only to applicable laws
// and regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE
// AT ALL TIMES.

// This module generates AXI ethernet traffic based on user inputs
// enable_gen - Allowed values (0,1); disable/enable the traffic generator 
// data_payload  - Allowed values (46 - 1500 bytes); data payload size

// Packet format of the ethernet packets generated by this block
//  -------------------------------------------------------------------------
//  | byte 8 | byte 7 | byte 6 | byte 5 | byte 4 | byte 3 | byte 2 | byte 1 |
//  -------------------------------------------------------------------------
//  | Source Address  |           Destination Address                       |
//  -------------------------------------------------------------------------
//  |     SeqNo       |     Length      |       Source Address              |
//  -------------------------------------------------------------------------
//  |  ...............................  |      SeqNo      |     SeqNo       |
//  -------------------------------------------------------------------------
//  |     SeqNo       | ................................................... |
//  -------------------------------------------------------------------------

// Sequence number incrememts by one every packet.

`timescale 1ps / 1ps

module axi_stream_gen #(
    parameter AXIS_TDATA_WIDTH =  64,
    parameter AXIS_TKEEP_WIDTH =  AXIS_TDATA_WIDTH/8,   
    parameter XIL_MAC_ID_THIS  =  48'h111100000000, // 00:00:00:00:11:11
    parameter XIL_MAC_ID_OTHER =  48'h222200000000  // 00:00:00:00:22:22
)
(
    input                         reset,   
    input                         tx_axis_clk,

    output [AXIS_TDATA_WIDTH-1:0] tx_axis_tdata,
    output [AXIS_TKEEP_WIDTH-1:0] tx_axis_tkeep,
    output                        tx_axis_tvalid ,
    output                        tx_axis_tlast,
    output                        tx_axis_tuser,
    input                         tx_axis_tready,

    input                         enable_gen,
    input  [15:0]                 packet_count,
    input  [15:0]                 data_payload

);

  // Ethernet header is 128 bits, this includes 16 bits of data payload
  // see packet format above
  // Ethernet Header Count defines the number of ethernet header DWORDS
  // 1 DWORD =  AXIS_TDATA_WIDTH bits
  localparam [3:0]   ETH_HEADER_CNT  = 128/AXIS_TDATA_WIDTH;

  wire [15:0]                   pkt_length;
  
  wire [127:0]                  ethernet_header;
  reg  [3:0]                    hd_cnt_g = 0;

  reg  [AXIS_TDATA_WIDTH-1:0]   tx_data_g = 'b0;
  reg                           tx_data_valid_g = 'b0;
  reg                           tx_data_last_g = 'b0;
  reg  [AXIS_TKEEP_WIDTH-1:0]   tx_data_keep_g = 'b0;
  reg  [AXIS_TKEEP_WIDTH/2-1:0] invalid_bytes = 'b0;
  reg  [15:0]                   byte_cnt_g = 'b0;
  reg  [15:0]                   seq_no_g = 'b0;
  reg                           packet_count_done = 'b0;

  // enumerated states
  localparam IDLE       = 1'b0;
  localparam GEN_PKT    = 1'b1;
  
  reg  cstate = 1'b0;   // current state
  reg  nstate = 1'b0;   // next state

  // Current and Next state logic
  always @ (posedge tx_axis_clk) begin
    if (reset == 1'b1)
      cstate <= IDLE;
    else 
      cstate <= nstate;
  end
  

  // if enable generator is set to 0 in a middle of the packet,
  // the generator completes the packet and then stops generating 
  // any more packets
  always@(cstate, enable_gen, byte_cnt_g, tx_axis_tready) begin
    case (cstate)
      IDLE: begin
        if (enable_gen && !packet_count_done) begin
          nstate <= GEN_PKT;
        end else begin
          nstate <= IDLE;
        end
      end
  
      GEN_PKT: begin
        if (byte_cnt_g <= AXIS_TKEEP_WIDTH && tx_axis_tready) begin
          nstate <= IDLE;
        end else begin
          nstate <= GEN_PKT;
        end
      end
  
      default : begin
        nstate <= IDLE;
      end
  
    endcase
  end
  
 // 14 bytes header = destination address     (6 bytes) +
 //                   source address          (6 bytes) +
 //                   type/len                (2 bytes) +
 //                   fcs is generated by MAC (0 bytes)
 // Add 14 bytes of header to payload byte count to get packet length
 // data_payload is supplied by the user - min = 64 bytes, max = 1500 bytes
 assign pkt_length = data_payload + 'd14;
 
 // Counting down data bytes 
 always @(posedge tx_axis_clk)
  begin
    // hold value if tready is not asserted
    if (!tx_axis_tready && tx_data_valid_g)
      byte_cnt_g <= byte_cnt_g;
    else if (cstate == IDLE) 
      byte_cnt_g <= pkt_length - AXIS_TKEEP_WIDTH;
    else if (byte_cnt_g < AXIS_TKEEP_WIDTH) 
      byte_cnt_g <= 0;
    else 
      byte_cnt_g <= byte_cnt_g - AXIS_TKEEP_WIDTH;
  end

 // Counting ethernet header DWORDs
 // 1 DWORD =  AXIS_TDATA_WIDTH bits
 always @(posedge tx_axis_clk)
  begin
    // hold value if tready is not asserted
    if (!tx_axis_tready && tx_data_valid_g)
      hd_cnt_g <= hd_cnt_g;
    else if (cstate == IDLE) 
      hd_cnt_g <= 1;
    else if (hd_cnt_g == ETH_HEADER_CNT) 
      hd_cnt_g <= hd_cnt_g;
    else 
      hd_cnt_g <= hd_cnt_g + 1;
  end

  // Sequence number generation
  // increments by 1 @ the end of a packet
  // and is used as data for the next packet  
  always @(posedge tx_axis_clk)
  begin
    if (!enable_gen && cstate == IDLE) 
      seq_no_g <= 0;
    else if (cstate == GEN_PKT && nstate == IDLE) 
      seq_no_g <= seq_no_g + 1;
  end

  always @(posedge tx_axis_clk)
  begin
    if (packet_count == 16'b0 || !enable_gen) 
      packet_count_done <= 1'b0;
    else if (seq_no_g == packet_count-1) 
      packet_count_done <= 1'b1;
  end
  
  // tvalid generation
  always @(posedge tx_axis_clk)
  begin
    if (!enable_gen && cstate == IDLE) 
      tx_data_valid_g <= 1'b0;
    // hold value if tready is not asserted
    else if (!tx_axis_tready && tx_data_valid_g)
      tx_data_valid_g <= tx_data_valid_g;
    else 
      tx_data_valid_g <= 1'b1;
  end


  // tkeep generation 
  always @(posedge tx_axis_clk)
  begin
    if (cstate == IDLE) begin
      invalid_bytes <= AXIS_TKEEP_WIDTH - (pkt_length % AXIS_TKEEP_WIDTH);
    end
    
    // hold value if tready is not asserted
    if (!tx_axis_tready && tx_data_valid_g)
      tx_data_keep_g <= tx_data_keep_g;
    else if (cstate == GEN_PKT && nstate == IDLE && invalid_bytes < AXIS_TKEEP_WIDTH) 
      tx_data_keep_g <= {AXIS_TKEEP_WIDTH{1'b1}} >> invalid_bytes;
    else  
      tx_data_keep_g <= {AXIS_TKEEP_WIDTH{1'b1}};
  end
  
  
  // tlast generation 
  always @(posedge tx_axis_clk)
  begin
    // hold value if tready is not asserted
    if (!tx_axis_tready && tx_data_valid_g)
      tx_data_last_g <= tx_data_last_g;
    else if (cstate == GEN_PKT && nstate == IDLE) 
      tx_data_last_g <= 1;
    else  
      tx_data_last_g <= 0;
  end
  

  // tdata generation
  // dst addr -  MAC ID of the mac which receives RX data
  assign ethernet_header [47:0]     = XIL_MAC_ID_OTHER;   
  // src addr -  MAC ID of the mac connected to this generator
  // which sends TX data
  assign ethernet_header [95:48]    = XIL_MAC_ID_THIS;
  assign ethernet_header [111:96]   = {data_payload[7:0], data_payload[15:8]};
  assign ethernet_header [127:112]  = seq_no_g;

  always @(posedge tx_axis_clk)
  begin
    // hold value if tready is not asserted
    if (!tx_axis_tready && tx_data_valid_g)
      tx_data_g <= tx_data_g;
    // Ethernet header
    else if (cstate == IDLE ) 
      tx_data_g <=  ethernet_header[AXIS_TDATA_WIDTH-1:0];
    // Ethernet header
    else if (hd_cnt_g != ETH_HEADER_CNT ) 
      tx_data_g <=  ethernet_header[(AXIS_TDATA_WIDTH * hd_cnt_g)+:AXIS_TDATA_WIDTH];
    // Payload
    else  
      tx_data_g <=  {AXIS_TKEEP_WIDTH{seq_no_g}};
  end
  
  // Tramsit Data
  assign  tx_axis_tdata  =  tx_data_g; 
  assign  tx_axis_tvalid =  tx_data_valid_g;
  assign  tx_axis_tkeep  =  tx_data_keep_g; 
  assign  tx_axis_tlast  =  tx_data_last_g; 
  assign  tx_axis_tuser  =  1'b0; 

endmodule
